枚舉是 自定義一組具名常數值
的特殊型別,每個常數值都有一個名稱和一個關聯的數值,主要用於取值限定在一定的範圍內。在 TypeScript 中,我們使用 enum
關鍵字來定義枚舉。
那為什麼要定義枚舉值呢?因為後端工程師在開狀態的時候,有時會用 0、1、2 之類來分類狀態,如果我們或後端工程師都沒寫註解來說明數字代表什麼意思時,其他工程師可能短時間內會無法理解而浪費開發時間,把枚舉值定義清楚後,這樣就一目瞭然了。
在 TypeScript 裡自定義型別,通常開頭首字母 大寫
,定義常數值 (枚舉成員) 時如果沒有賦值,第一個參數會默認為 0,之後會逐步遞增 1 (後面如果沒有賦值,會自動依據前面的值自動 +1)。
看以下範例:
enum Status {
Error, // 0
Success, // 1
Warning, // 2
Info, // 3
}
我們定義一個 Status
的枚舉值,其中包含了四個成員:Error
、Success
、Warning
和 Info
。在預設情況下,這些成員的數值會從 0 開始遞增,分別是 0、1、2
和 3
。
接著我們嘗試修改枚舉成員 Success 的值變為 5
:
enum Status {
Error, // 0
Success = 5, // 5
Warning, // 6
Info, // 7
}
可以看到數值變成 0
和 5、6、7
。
當然我們也可以自訂枚舉成員的數值:
enum Status {
Error = "ERROR",
Success = "SUCCESS",
Warning = "WARNING",
Info = "INFO",
}
不過要特別注意的是 賦值不要出現重複
,否則會進行覆蓋。
我們又再次修改把枚舉成員 Success 的值變為 0
,看會發生什麼:
enum Status {
Error, // 0
Success = 0, // 0
Warning, // 1
Info, // 2
}
console.log(Status.Error); // 輸出: 0
console.log(Status[0]); // 輸出: Success
可以看到原本的 0: "Error" 已經被 Success 覆蓋了,變成 0: "Success"
。
我們來個小練習:如果要定義一個 Weekday
的枚舉值來代表星期,星期日要為 7,星期一為 1,以此類推
,那該怎麼定義呢?
enum Weekday = {
Sun = 7, // 7
Mon = 1, // 1
Tue, // 2
Wed, // 3
Thu, // 4
Fri, // 5
Sat // 6
}
既然我們都會定義枚舉值了,那該如何使用枚舉值呢?
假設我們要做一個遊戲,方向有上下左右 (0、1、2、3),而取得使用者的方向為 0
,該如何讓程式知道使用者往哪個方向走了呢?
看以下範例:
enum Direction {
Up,
Down,
Left,
Right,
}
let userDirection = 0; // 假設取得的值為 0
let userWay: string;
if (userDirection === Direction.Up) {
userWay = "威爾豬往上走";
} else if (userDirection === Direction.Down) {
userWay = "威爾豬往下走";
} else if (userDirection === Direction.Left) {
userWay = "威爾豬往左走";
} else {
userWay = "威爾豬往右走";
}
console.log(userWay); // 輸出: 威爾豬往上走
這次我們修改使用者的方向為 2
,並使用 Switch Case 的方式改寫:
enum Direction {
Up,
Down,
Left,
Right,
}
let userDirection = 2; // 假設取得的值為 2
let userWay: string;
switch (userDirection) {
case Direction.Up:
userWay = "威爾豬往上走";
break;
case Direction.Down:
userWay = "威爾豬往下走";
break;
case Direction.Left:
userWay = "威爾豬往左走";
break;
default:
userWay = "威爾豬往右走";
break;
}
console.log(userWay); // 輸出: 威爾豬往左走
我們再看另一個例子,如果要依對象的性別來打招呼,該怎麼做呢?
enum Gender {
Male,
Female,
Other,
}
const greetUser = (name: string, gender: number): string => {
let greeting: string;
if (gender === Gender.Male) {
greeting = `哈囉,${name}帥哥`;
} else if (gender === Gender.Female) {
greeting = `哈囉,${name}美女`;
} else {
greeting = `哈囉,尊貴的${name}`;
}
console.log(greeting);
return greeting;
};
greetUser("威爾豬", 0); // 輸出: 哈囉,威爾豬帥哥
greetUser("威爾羊", 1); // 輸出: 哈囉,威爾羊美女
greetUser("威爾牛", 2); // 輸出: 哈囉,尊貴的威爾牛
如果我們需要列出枚舉成員的話,我們可以使用 for...in
。但需要注意的是,這樣不僅會列出枚舉成員,還會列出 enum 的其他屬性。
enum Status {
Error,
Success,
Warning,
Info,
}
for (let key in Status) {
console.log(key); // 輸出: 0 1 2 3 Error Success Warning Info
}
如果只想列出枚舉成員,可以使用以下方式:
enum Status {
Error,
Success,
Warning,
Info,
}
for (let key in Status) {
if (typeof Status[key] === "number") {
console.log(key); // 輸出: Error Success Warning Info
}
}
這是在 TypeScript 中 結合兩個或多個枚舉值來創建更複雜的結構
,這樣可以通過將多個枚舉值合併到一個新的枚舉值中,不過威爾豬很少會這樣做。
假設我們有兩個枚舉值 Phone 和 Color,然後我們將 Phone 和 Color 的枚舉成員合併到一個新的枚舉值 PhoneAndColor 中,以實現結合的效果。看以下範例:
// 合併枚舉
enum Phone {
Apple,
Samsung,
Mi,
}
enum Color {
Gold,
Yellow,
White,
}
enum PhoneAndColor {
Apple = Phone.Apple, // 0
Samsung = Phone.Samsung, // 1
Mi = Phone.Mi, // 2
Gold = Color.Gold, // 0
Yellow = Color.Yellow, // 1
White = Color.White, // 2
}
const selectPhone: PhoneAndColor = PhoneAndColor.Apple;
const selectColor: PhoneAndColor = PhoneAndColor.White;
console.log(PhoneAndColor);
console.log(selectPhone); // 輸出: 0
console.log(selectColor); // 輸出: 2
console.log(PhoneAndColor[1]) // 輸出: Yellow
需注意的是,Color 會覆蓋掉 Phone:0: "Gold"、1: "Yellow"、2: "White"
。
我們也可以使用 const enum
,在編譯時將枚舉值直接轉換為常數,這樣定義就 不會在 .js 裡被編譯出來,從而減少生成的程式碼
,我們可以看看相同程式碼編譯出來的 JavaScript 文件:
// 未使用 const
enum Gender {
Male,
Female,
Other,
}
const gender = Gender.Female;
console.log(gender); // 輸出: 1
// 使用 const
const enum Gender {
Male,
Female,
Other,
}
const gender = Gender.Female;
console.log(gender); // 輸出: 1
這對於優化文件的大小很有幫助,在某些情況下也可以提高性能,但同時也有一些缺點和限制:
無法動態生成枚舉成員: 常數枚舉的成員在編譯時就已經被解析為字面值
,表示我們無法基於某些條件來動態添加或刪除枚舉成員。
無法進行反向查找: 常數枚舉 無法透過數值來查找對應的枚舉成員名稱
,這在某些情況下可能不太方便使用。
不能被導出: 常數枚舉 不能被導出到外部模組來重複使用
。
總而言之,常數枚舉在我們需要優化性能,並確保枚舉值在編譯時被完全替換為數值時非常有用,然而它的限制可能在某些情況下變得不太方便。因此,我們要仔細考慮需求,並選擇適合的枚舉值類型。
透過定義枚舉,我們可以更清楚地表示一組相關的常數值 (枚舉成員),無論是用於表示狀態、選項或其它具有固定含義的值,枚舉值都有助於使程式碼更具結構性和可理解性
,減少我們需要去翻文件或詢問的時間。